package index;


import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.List;

import datatableaux.Reason;

import algebra.OrderingDescription;

import kb.DescriptionLogicsKB;
import utils.exception.OrderingDescriptionException;
import utils.io.KBAccess;
import entities.Assertion;
import entities.ConceptAssertion;

public class DescriptionIndex {
	//public static final String ID_FEATURE_NAME = "_id";
	
	private IndexTreeNode root;
	private OrderingDescription od;
	private DescriptionLogicsKB kb; //the KB used for reasoning
	private static boolean SAME_IND_RESTRICTION = true;
	
	//////// Constructors
	public DescriptionIndex(IndexTreeNode root, OrderingDescription od, DescriptionLogicsKB kb) {
		// WARNING: Shallow copy!
		this.root = root;
		this.od = od;
		this.kb = kb;
		this.od.setKB(kb);
//		this.od.setTBox(kb.getTbox());
//		this.od.setABox(kb.getAbox());
	}
	
	public DescriptionIndex(OrderingDescription od) {
		this.od = od;
	}
	
	public DescriptionIndex(String od, DescriptionLogicsKB kb) throws OrderingDescriptionException {
		this.od = new OrderingDescription(od, kb);
		this.od.setKB(kb);
//		this.od.setTBox(kb.getTbox());
//		this.od.setABox(kb.getAbox());
	}
	
	/////// Utilities
	public void insert(ConceptAssertion ac) throws OrderingDescriptionException {
		if (root == null) {
			root = new IndexTreeNode(ac);
		}
		else {
			root = TreeUtils.insert(root, od, ac);
		}
	}
	
	public void remove(ConceptAssertion ac) throws OrderingDescriptionException {
		TreeUtils.remove(root, od, ac);
	}
	
	public List<ConceptAssertion> search(ConceptAssertion query) throws OrderingDescriptionException {
		if (this.root == null) {
			return new LinkedList<ConceptAssertion>();
		}	
		LinkedList<ConceptAssertion> res = new LinkedList<ConceptAssertion>();
		search(query, root, res);
		return res;
	}
	
	public void search(ConceptAssertion query, IndexTreeNode node, List<ConceptAssertion> result) 
				throws OrderingDescriptionException {
		
		if (node == null) {
			return;
		}else{
			this.searchSingleNodeWithCompare(query, node, result);
		}

	}
	
	private void searchSingleNodeWithCompare(ConceptAssertion query, 
			IndexTreeNode node, 
			List<ConceptAssertion> result) throws OrderingDescriptionException{
		int compare = od.compare(query, node.getNodeAssertion()); 
		
		if (compare < 0) {
			search(query, node.getLeft(), result);			
		}
		else if (compare > 0) {
			search(query, node.getRight(), result);
		}
		else {
			search(query, node.getLeft(), result);			
			/* add this as a query result candidate if entailment holds*/
			if(SAME_IND_RESTRICTION && 
					query.getIndividual().equals(node.getNodeAssertion().getIndividual())){
				//check if T U {a:assert_node} |= a:query
				Reason res = new Reason(false);	
//				if(node.getNodeAssertion().getIndividual().toString().contains("m9_")){
//					System.out.println("here:");
//				}
				if(res.ABoxUnionAssertionEntailAssertionCheck(node.getNodeAssertion(), 
						query, kb)){
//					System.out.println("True: "+node.getNodeAssertion()+" -> "+query);
					result.add(node.getNodeAssertion());
				}
			}
			search(query, node.getRight(), result);
		}
	}
	
	/////// Getters and Setters
	public IndexTreeNode getRoot() {
		return root;
	}

	public void setRoot(IndexTreeNode root) {
		this.root = root;
	}

	public OrderingDescription getOd() {
		return od;
	}

	public void setOd(OrderingDescription od) {
		this.od = od;
	}

	public void print(OutputStream out) {
		if (this.root == null) {
			return;
		}
		this.root.print(out, "", "\n");
	}
	
	public void print(OutputStream out, String prefix) {
		if (this.root == null) {
			return;
		}
		this.root.print(out, prefix, "\n");
	}
	
	public void print(OutputStream out, String prefix, String newline) {
		if (this.root == null) {
			return;
		}
		this.root.print(out, prefix, newline);
	}
	
	public void load(String filename) {
		FileReader fr = null;
		BufferedReader br = null;
		try{
		fr = new FileReader(filename);
		br = new BufferedReader(fr);
		this.load(br);
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try {
				fr.close();
				br.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}
	
	public void load(BufferedReader in) throws IOException, OrderingDescriptionException {
		for(Assertion ast:KBAccess.processAssertionFile(in, false)){
			if(ast instanceof ConceptAssertion){
				this.insert((ConceptAssertion)ast);
			}
		}
	}

	
	public void load(List<ConceptAssertion> cas) throws OrderingDescriptionException{
		for(ConceptAssertion ca:cas){
			this.insert(ca);
		}
	}

	
}
